function getRemoteImage(): Promise<HTMLImageElement> {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      const image = new Image();
      image.src = "./goupi.png";
      image.onload = () => resolve(image);
      image.onerror = reject;
    }, 1000);
  });
}

/**
 * @description: 获取坐标像素点数据的方法
 * @param {ImageData} imageData
 * @param {number} x
 * @param {number} y
 * @return {number[]}
 */
function getPxColor(imageData: ImageData, x: number, y: number): number[] {
  // 一个像素点颜色由4个值组成
  let color = [];
  let width = imageData.width;
  color[0] = imageData.data[(y * width + x) * 4];
  color[1] = imageData.data[(y * width + x) * 4 + 1];
  color[2] = imageData.data[(y * width + x) * 4 + 2];
  color[3] = imageData.data[(y * width + x) * 4 + 3];
  return color;
}
/**
 * @description: 设置某个坐标的像素值
 * @param {ImageData} imageData
 * @param {number} x
 * @param {number} y
 * @param {number} color
 */
function setPxColor(
  imageData: ImageData,
  x: number,
  y: number,
  color: number[],
) {
  let width = imageData.width;
  imageData.data[(y * width + x) * 4] = color[0];
  imageData.data[(y * width + x) * 4 + 1] = color[1];
  imageData.data[(y * width + x) * 4 + 2] = color[2];
  imageData.data[(y * width + x) * 4 + 3] = color[3];
}

/**
 * @description: 生成马赛克图像 用作高速马赛克基层
 * @param {CanvasElement} canvas
 * @param {CanvasCtx} ctx
 * @param {HTMLImageElement} image
 * @return {CanvasElement}
 */
function generateOriginMosaic(
  canvas: CanvasElement,
  ctx: CanvasCtx,
  image: HTMLImageElement,
): CanvasElement {
  if (!canvas || !ctx) return null;
  // 在 canvas 上绘制原始图片
  ctx.drawImage(image, 0, 0, image.width, image.height);

  // 获取图片的像素数据
  let oldImage = ctx.getImageData(0, 0, image.width, image.height);

  // 创建一个新的变量存储马赛克像素数据
  let newImage = ctx.createImageData(image.width, image.height);

  // 定义一个size为x的大小的块(x个像素为一个单位)，整个图片横向分为width/size块；纵向分为height/size块
  let size = 2;
  for (let i = 0; i < oldImage.width / size; i++) {
    for (let j = 0; j < oldImage.height / size; j++) {
      // 从一个块里找到某个随机像素点，获取这个像素点的颜色
      let x = Math.floor(Math.random() * size);
      let y = Math.floor(Math.random() * size);
      let color = getPxColor(oldImage, i * size + x, j * size + y);
      // 把颜色重新绘制到每个小块中
      for (let k = 0; k < size; k++) {
        for (let t = 0; t < size; t++) {
          setPxColor(newImage, i * size + k, j * size + t, color);
        }
      }
    }
  }

  ctx.clearRect(0, 0, image.width, image.height);
  ctx.putImageData(newImage, 0, 0);
  return canvas;
}

/**
 * @description: 生成马赛克图像
 * @param {HTMLImageElement} image
 * @param {CanvasElement} canvas
 * @param {CanvasCtx} ctx
 * @return {Promise<HTMLImageElement>}
 */
async function generateMosaicImage(
  image: HTMLImageElement,
  canvas: CanvasElement,
  ctx: CanvasCtx,
): Promise<HTMLImageElement> {
  return new Promise(async (resolve, reject) => {
    const MosaicCanvas = generateOriginMosaic(
      canvas,
      ctx,
      image,
    ) as unknown as HTMLCanvasElement;
    const MosaicImage = await convertCanvasToImage(MosaicCanvas);
    resolve(MosaicImage);
  });
}

/**
 * @description: canvas to image
 * @param {HTMLCanvasElement} canvas
 * @return {Promise<HTMLImageElement>}
 */
async function convertCanvasToImage(
  canvas: HTMLCanvasElement,
): Promise<HTMLImageElement> {
  return new Promise((resolve, reject) => {
    const image = new Image();
    image.src = canvas.toDataURL();
    image.onload = () => resolve(image);
    image.onerror = reject;
  });
}

type CanvasElement = HTMLCanvasElement | null;
type CanvasCtx = CanvasRenderingContext2D | null;
let Canvas: CanvasElement, CanvasCtx: CanvasCtx;
/**
 * @description: canvas加工生成马赛克图像
 * @return {HTMLImageElement}
 */
export async function useMosaicImage(
  targetCanvas: HTMLCanvasElement,
  tarCtx: CanvasRenderingContext2D,
): Promise<HTMLImageElement> {
  const image = await getRemoteImage();
  Canvas = document.createElement("canvas");
  CanvasCtx = Canvas.getContext("2d") as unknown as CanvasRenderingContext2D;
  // 设置 canvas 的宽度和高度与图片相同
  Canvas.width = image.width;
  Canvas.height = image.height;

  const MosaicImage = await generateMosaicImage(image, Canvas, CanvasCtx);
  Canvas = null;
  CanvasCtx = null;
  return MosaicImage;
  // document.body.appendChild(MosaicImage);

}
